﻿using System.Xml;
using System;
using System.Reflection;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Xml.Xsl;
using System.IO;
using System.Text;

namespace OrganizationName.Common
{
    internal class Utility
    {
        internal static int GetIndexOfXmlNodeInParent(XmlNode node)
        {
            string nodeName = node.Name;
            int index = -1;
            foreach (XmlNode childNode in node.ParentNode.ChildNodes)
            {
                if (childNode.Name != nodeName)
                {
                    continue;
                }
                else if (childNode == node)
                {
                    return index + 1;
                }
                else
                {
                    index++;
                }
            }
            return -1;
        }

        internal static int GetIndexOfElementInArray(Array array, object element)
        {
            int index = -1;
            foreach (object childElement in array)
            {
                if (childElement == element)
                {
                    return index + 1;
                }
                index++;
            }
            return -1;
        }

        internal static System.Type[] GetSupportedTypes(System.Type classType, bool includeHierarchy, bool includeInterfaces)
        {
            List<System.Type> classHierarchy = new List<System.Type>();
            classHierarchy.Add(classType);

            System.Type baseType = classType.BaseType;

            if (includeHierarchy)
            {
                while (baseType != null)
                {
                    classHierarchy.Add(baseType);
                    baseType = baseType.BaseType;
                }
            }

            if (includeInterfaces)
            {
                classHierarchy.AddRange(classType.GetInterfaces());
            }

            return classHierarchy.ToArray();
        }

        internal static System.Type[] GetCollectionAddMethodParametersIfNotAmbigious(System.Type classType)
        {
            MethodInfo addMethod;

            try
            {
                addMethod = classType.GetMethod("Add", BindingFlags.Public | BindingFlags.Instance);
            }
            catch (AmbiguousMatchException)
            {
                return null;
            }

            ParameterInfo[] addMethodParameters = addMethod.GetParameters();

            if (addMethodParameters.Length == 1 || addMethodParameters.Length == 2)
            {
                List<System.Type> parameterTypes = new List<System.Type>();
                foreach (ParameterInfo parameter in addMethodParameters)
                {
                    parameterTypes.Add(parameter.ParameterType);
                }
                return parameterTypes.ToArray();
            }
            else
            {
                return null;
            }
        }

        internal static System.Type[] GetCollectionKeyAndElementTypes(System.Type classType)
        {
            List<System.Type> allTypes = new List<System.Type>(GetSupportedTypes(classType, true, true));

            foreach (System.Type subClassType in allTypes)
            {
                if (subClassType.IsInterface && subClassType.Name == "IDictionary`2" && subClassType.Namespace == "System.Collections.Generic")
                {
                    return new System.Type[] { subClassType.GetGenericArguments()[0], subClassType.GetGenericArguments()[1] };
                }
            }

            foreach (System.Type subClassType in allTypes)
            {
                if (subClassType.IsInterface && subClassType.Name == "IList`1" && subClassType.Namespace == "System.Collections.Generic")
                {
                    return new System.Type[] { subClassType.GetGenericArguments()[0] };
                }
            }

            if (allTypes.Contains(typeof(NameObjectCollectionBase)))
            {
                return new System.Type[] { typeof(string), typeof(object) };
            }

            else if (allTypes.Contains(typeof(StringCollection)))
            {
                return new System.Type[] { typeof(string) };
            }

            else if (allTypes.Contains(typeof(StringDictionary)))
            {
                return new System.Type[] { typeof(string), typeof(string) };
            }

            else if (allTypes.Contains(typeof(System.Collections.Specialized.OrderedDictionary)))
            {
                return new System.Type[] { typeof(object), typeof(object) };
            }

            else if (allTypes.Contains(typeof(IDictionary)))
            {
                return new System.Type[] { typeof(object), typeof(object) };
            }

            else if (allTypes.Contains(typeof(IList)))
            {
                return new System.Type[] { typeof(object) };
            }

            throw new System.ArgumentException("The type " + classType + " is not a collection, or is of a type of collection that is not supported by the current version");
        }

        internal static bool IsCreatable(System.Type classType)
        {
            if (classType.IsInterface || classType.IsNotPublic)
            {
                return false;
            }
            return true;
        }

        public static Stream CompileXml(string xmlFilePath, string xslFilePath)
        {
            XslCompiledTransform compiledTransform = new XslCompiledTransform();
            compiledTransform.Load(xslFilePath);
            MemoryStream transformedXmlStream = new MemoryStream();
            System.Xml.XmlWriter writer = new System.Xml.XmlTextWriter(transformedXmlStream, Encoding.UTF8);
            compiledTransform.Transform(xmlFilePath, writer);
            transformedXmlStream.Position = 0;
            return transformedXmlStream;
        }

        public static FileSystemWatcher MonitorFileForModification(string filePath, FileSystemEventHandler modificationHandler)
        {
            string fileFullPath = Path.GetFullPath(filePath);
            string fileDirectory = Path.GetDirectoryName(fileFullPath);
            string fileName = Path.GetFileName(fileFullPath);
            FileSystemWatcher fileModificationMonitor = new FileSystemWatcher(fileDirectory, fileName);
            fileModificationMonitor.NotifyFilter = NotifyFilters.LastWrite;
            fileModificationMonitor.Changed += modificationHandler;
            fileModificationMonitor.EnableRaisingEvents = true;
            return fileModificationMonitor;
        }
    }
}
